/**
 * @page router: 每个页面组件都需要绑定此属性.才可以使用该页面的页面方法
 * {
 *  key: 每次切换页面使用的key,预设为showIndex
 *  fn: 跳转后会执行的方法. 为String 时,将取component内的对应方法.
 *  name: 组件路由名称, 注册多个路由时必填, 同名组件会被覆盖 不传时.默认为组件路径(没有首字符 /)
 *  parent: 子组件必传,父组件的name,不传入时,为注册根路由. 一个项目内只有一个根路由,多次注册根路由不会被覆盖
 *  index: 子组件必传,对应在父组件中,显示该组件时所设定的值
 *  destroy: 需要在组件中分离onShow 与 onLoad 时.传入的销毁key
 *     传入此属性时,需要将key 对应 hidden, destroy 对应wx:if
 *  default: 该组件默认显示的子组件索引
 * }
 *
 * 不论是使用getRouter方法, 或者this.router,只能在某个页面组件内ready的生命周期函数被执行之后才可以获取到 router 对象.
 */

//conveyParams 简单处理参数过渡. 因为新页面子router未创建出来. 无法直接挂载
let router,
  currentComponent,
  currentRouter,
  history = [],
  nowPage = '/',
  conveyParams = {}

/**
 * @param option:
 * {
 *  key: 每次切换页面使用的key,预设为showIndex
 *  fn: 跳转后会执行的方法. 为String 时,将取component内的对应方法.
 *  name: 组件路由名称, 注册多个路由时必填, 同名组件会被覆盖
 * }
 *
 * @param component 该路由所在组件, 通常传入this
 * @param parent 父组件的name,不传入时,为注册根路由. 一个项目内只有一个根路由,多次注册根路由不会被覆盖
 */
class Router {
  constructor (option, component, parent) {
    const parentrouter = getRouter(parent) || null
    if (!component) {
      throw new Error('未传入第二个参数: this')
    }
    const fn = option.fn
    if (typeof fn === 'function') {
      this.fn = fn
    } else if (typeof fn === 'string') {
      let handle = component[fn] || component.methods[fn]
      if (handle && typeof handle === 'function') {
        this.fn = handle
      } else {
        throw new Error('fn必须为一个function 或者 方法名')
      }
    }
    this.component = component
    this.is = component.is
    this.key = option.key || 'showIndex'
    this.hideKey = option.hideKey
    this.name = option.name || component.is
    this.index = option.index ? option.index + '' : null
    this.childRouters = []
    this.route = {}
    this.parentrouter = parentrouter
    this.page = parentrouter ? parentrouter.page + option.index + '/' : '/'
    this.default = option.default || 0
    this.params = {}
    // TODO 页面切换的animte效果
  }

  get length () {
    return history.length
  }

  pathResolve (path) {
    if (path[0] === '/') {
      let pathArr = path.split('/')
      let nowLen = currentRouter.page.split('/').length - 2
      pathArr.shift()
      pathArr.pop()
      let parent = getRouter()
      if (pathArr.length) {
        pathArr.forEach((v, i, a) => {
          parent.changeChildPage(v)
          if (a.length - 1 !== i) {
            parent = parent.getChild(v)
          } else if (nowLen > pathArr.length) {
            parent = parent.getChild(v)
            parent.changeChildPage(parent.default)
          }
        })
      } else {
        router.changeChildPage(router.default)
      }
    } else {
      if (~(path + '').indexOf('/')) {
        throw new Error('暂时不支持在子组件内跳转多级目录, 请传入子组件index 或使用绝对目录路径')
      }
      if (path !== this.index) {
        this.changeChildPage(path)
      }
    }
    this.fn && this.fn.call(this.component, path)
  }

  changeChildPage (index) {
    let page = {}
    page[this.key] = index
    this.component.setData(page)
    currentRouter = this.getChild(index) || this
    if (currentRouter) {
      currentComponent = currentRouter.component
      //todo 区分处理show与load
      // currentRouter.onShow && currentRouter.onShow()
    }
  }

  navigateTo (path, params = {}) {
    conveyParams = params
    this.route = {
      path: currentRouter.page,
      params: currentRouter.params
    }
    this.redirectTo(path, params)
    history.push(this.route)
    return this.length
  }

  navigateBack () {
    if (history.length > 0) {
      const route = history.pop()
      this.redirectTo(route.path, route.params)
      return this.length
    } else {
      if (getCurrentPages().length > 1) {
        wx.navigateBack()
      }
    }
  }

  redirectTo (path, params = {}) {
    this.pathResolve(path)
    conveyParams = params
  }

  getChild (index) {
    for (let i = 0; i < this.childRouters.length; i++) {
      if (this.childRouters[i].index == index) {
        return this.childRouters[i]
      }
    }
    return null
  }
}

/**
 * 初始化路由
 */
function init () {
  let _Page = Page
  let _Component = Component

  Page = function (config) {
    let pageConfig = pageEventHook()(config)
    pageConfig = bindRouter(pageConfig)
    _Page(pageConfig)
  }

  Component = function (config) {
    let pageConfig = bindRouter(config)
    _Component(pageConfig)
  }
}

/**
 * 依照router配置注册路由的主方法
 */
function bindRouter () {
  let pageConfig = arguments ? arguments[0] : void 0
  let routerConfig = pageConfig.router
  let originalOnload = pageConfig.ready || pageConfig.onLoad
  pageConfig.onLoad = pageConfig.ready = function () {
    if (routerConfig) {
      this.router = extend(routerConfig, this)
      this.router.params = conveyParams
    }
    this.setData({
      isIphoneX: getApp().globalData.isIphoneX
    })
    // if (pageConfig.methods && pageConfig.methods.onShow) {
    //   this.router.onShow = pageConfig.methods.onShow.bind(this)
    // }
    originalOnload && originalOnload.call(this)
    // 在原 onLoad 执行后再切换出默认组件。避免页面组件attached比页面的onLoad更早执行
    if (this.router) {
      this.router.changeChildPage(this.router.default)
    }
  }
  return pageConfig
}

/**
 * 挂载页面事件钩子.可以更方便在组件页面中直接使用相应的Page的事件.
 */
function pageEventHook () {
  let pageHandlers = ["onShareAppMessage", "onPullDownRefresh", "onReachBottom", "onPageScroll"]
  return function hook () {
    let pageConfig = arguments ? arguments[0] : void 0
    pageHandlers.forEach(v => {
      let originalEvent = pageConfig[v]
      pageConfig[v] = function (e) {
        if (currentComponent === this) {
          return originalEvent && originalEvent.call(this, e)
        } else if (currentComponent && currentComponent[v]) {
          return currentComponent[v] && currentComponent[v].call(this, e)
        }
        return originalEvent && originalEvent.call(this, e)
      }
    })
    return pageConfig
  }
}

/**
 * 添加组件路由
 * 参数见Router构造器
 * @returns 返回此次注册的路由
 */
function extend (option, component) {
  let parent = option.parent,
    newRoute = new Router(option, component, parent)
  if (!parent) {
    currentRouter = router = option.isRoot ? newRoute : (router || newRoute)
    return router
  } else {
    mountChild(router, getRouter(parent), newRoute)
    return newRoute
  }
}

function mountChild (root, parent, child) {
  if (parent === root) {
    for (let i = 0; i < root.childRouters.length; i++) {
      if (root.childRouters[i].name === child.name) {
        root.childRouters[i] = child
        return
      }
    }
    root.childRouters.push(child)
  } else {
    root.childRouters.forEach(route => {
      mountChild(route, parent, child)
    })
  }
}

/**
 * 获取路由对象
 * @param name 注册路由时传入的路由名称
 * @param parent
 * @returns 返回名称对应的路由,没有参数时返回根路由
 *    除了根路由, 通过这个方法获取到的路由只能访问属性.不能调用方法
 */
function getRouter (name, parent = router) {
  if (!name) return router
  if (!parent || parent.name === name) {
    return parent
  } else {
    for (let i = 0; i < parent.childRouters.length; i++) {
      let router = getRouter(name, parent.childRouters[i])
      if (router) {
        return router
      }
    }
  }
}

export {init, extend, getRouter}
